﻿using Extended.WPF.Core.Helpers;
using Extented.UI.Core.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace System.Windows
{
    public static class FrameworkElementExtension
    {
#if !DXWINDOW
        public static bool IsLoaded(this FrameworkElement element)
        {
            return LayoutHelper.IsElementLoaded(element);
        }
        public static bool IsReallyVisible(this FrameworkElement obj)
        {
            return ((UIElement)obj).GetVisible() && obj.Width != 0 && obj.Height != 0;
        }
        public static void InvalidateMeasureEx(this UIElement obj)
        {
            IFrameworkElement elem = obj as IFrameworkElement;
            if (elem == null)
                return;
#if DEBUGTEST
			Size res = elem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
			((FrameworkElement)elem).Width = res.Width;
			((FrameworkElement)elem).Height = res.Height;
#else
            elem.InvalidateMeasureEx();
#endif
        }
        public static void MeasureEx(this UIElement obj, Size availableSize)
        {
#if DEBUGTEST
			IFrameworkElement elem = obj as IFrameworkElement;
			if (elem == null) {
				if (((FrameworkElement)obj).Parent != null)
					((FrameworkElement)obj).Measure(availableSize);
				return;
			}
			Size res = elem.Measure(availableSize);
			((FrameworkElement)elem).Width = res.Width;
			((FrameworkElement)elem).Height = res.Height;
#else
            obj.Measure(availableSize);
#endif
        }
        public static double GetLeft(this FrameworkElement element)
        {
            return GetPosition(element).X;
        }
        public static double GetTop(this FrameworkElement element)
        {
            return GetPosition(element).Y;
        }
        public static void SetLeft(this FrameworkElement element, double value)
        {
            Canvas.SetLeft(element, value);
        }
        public static void SetTop(this FrameworkElement element, double value)
        {
            Canvas.SetTop(element, value);
        }
        public static Point GetPosition(this FrameworkElement element)
        {
            return element.GetPosition(element.GetParent() as FrameworkElement);
        }
        public static Point GetPosition(this FrameworkElement element, FrameworkElement relativeTo)
        {
            return element.GetBounds(relativeTo).Location();
        }
        public static Size GetSize(this FrameworkElement element)
        {
            return UIElementExtension.GetRoundedSize(new Size(element.ActualWidth, element.ActualHeight));
        }
        public static Size GetVisualSize(this FrameworkElement element)
        {
            var slotSize = LayoutInformation.GetLayoutSlot(element).Size();
            SizeHelper.Deflate(ref slotSize, element.Margin);
            var result = element.GetSize();
            result.Width = Math.Min(result.Width, slotSize.Width);
            result.Height = Math.Min(result.Height, slotSize.Height);
            return result;
        }
        public static void SetSize(this FrameworkElement element, Size value)
        {
            element.Width = value.Width;
            element.Height = value.Height;
        }
        public static double GetRealWidth(this FrameworkElement element)
        {
            return Math.Max(element.MinWidth, Math.Min(element.Width, element.MaxWidth));
        }
        public static double GetRealHeight(this FrameworkElement element)
        {
            return Math.Max(element.MinHeight, Math.Min(element.Height, element.MaxHeight));
        }
        public static Size GetMinSize(this FrameworkElement element)
        {
            return new Size(element.MinWidth, element.MinHeight);
        }
        public static Size GetMaxSize(this FrameworkElement element)
        {
            return new Size(element.MaxWidth, element.MaxHeight);
        }
        public static Rect GetBounds(this FrameworkElement element)
        {
            return element.GetBounds(element.GetParent() as FrameworkElement);
        }
        public static Rect GetBounds(this FrameworkElement element, FrameworkElement relativeTo)
        {
            return element.MapRect(new Rect(new Point(0, 0), element.GetSize()), relativeTo);
        }
        public static Rect GetVisualBounds(this FrameworkElement element)
        {
            return element.GetVisualBounds(element.GetParent() as FrameworkElement, false);
        }
        public static Rect GetVisualBounds(this FrameworkElement element, FrameworkElement relativeTo)
        {
            return GetVisualBounds(element, relativeTo, true);
        }
        public static Rect GetVisualBounds(this FrameworkElement element, FrameworkElement relativeTo, bool checkParentBounds)
        {
            Rect result = element.MapRect(new Rect(new Point(0, 0), element.GetVisualSize()), relativeTo);
            if (checkParentBounds)
            {
                FrameworkElement parent = VisualTreeHelper.GetParent(element).FindElementByTypeInParents<FrameworkElement>(null);
                if (parent != null)
                    result.Intersect(parent.GetVisualBounds(relativeTo));
            }
            return result;
        }
        public static void SetBounds(this FrameworkElement element, Rect bounds)
        {
            if (!bounds.IsEmpty)
            {
                element.SetLeft(bounds.Left);
                element.SetTop(bounds.Top);
            }
            element.Width = bounds.Width;
            element.Height = bounds.Height;
        }
        public static bool IsVisible(this FrameworkElement element)
        {
            return element.IsVisible;
        }
        public static int GetZIndex(this FrameworkElement element)
        {
            return Canvas.GetZIndex(element);
        }
        public static void SetZIndex(this FrameworkElement element, int value)
        {
            Canvas.SetZIndex(element, value);
        }
#endif
        public static FrameworkElement GetRootParent(this FrameworkElement element)
        {
            FrameworkElement result;
            DependencyObject parent = element;
            do
            {
                result = (FrameworkElement)parent;
                parent = result.GetVisualParent();
            }
            while (parent != null);
            return result;
        }
        public static DependencyObject GetTemplatedParent(this FrameworkElement element)
        {
            return element.TemplatedParent;
        }
        public static FrameworkElement GetVisualParent(this FrameworkElement element)
        {
            DependencyObject parent = VisualTreeHelper.GetParent(element);
            while (parent != null && !(parent is FrameworkElement))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }
            if (parent == null && element.Parent is Popup)
                parent = (FrameworkElement)element.Parent;
            return parent != null ? (FrameworkElement)parent : null;
        }
        public static bool IsInVisualTree(this FrameworkElement element)
        {
            if (IsFullyTrusted)
                return PresentationSource.FromVisual(element) != null;
            else
                return element.GetRootParent() == Application.Current.MainWindow;
        }
        static bool IsFullyTrusted
        {
            get
            {
                return AppDomain.CurrentDomain.IsFullyTrusted;
            }
        }
        public static void RemoveFromVisualTree(this FrameworkElement obj)
        {
            DependencyObject parent = obj.GetParent();
            if (parent == null)
                return;
            ContentPresenter presenterParent = parent as ContentPresenter;
            if (presenterParent != null)
            {
                RemoveFromVisualTree(presenterParent);
            }
            ContentControl controlParent = parent as ContentControl;
            if (controlParent != null)
            {
                controlParent.Content = null;
            }
            Panel panelParent = parent as Panel;
            if (panelParent != null)
            {
                panelParent.Children.Remove(obj);
            }
        }
        public static DependencyObject GetParent(this FrameworkElement element)
        {
            DependencyObject result = element.Parent;
            if (result == null)
                result = VisualTreeHelper.GetParent(element);
            return result;
        }

        public static void SetParent(this FrameworkElement element, DependencyObject value)
        {
            if (element.Parent == value)
                return;
            DependencyObject parent = element.Parent;
            if (parent is Panel)
                ((Panel)parent).Children.Remove(element);
            else if (parent is Border)
                ((Border)parent).Child = null;
            if (value is Panel)
                ((Panel)value).Children.Add(element);
            else if (value is Border)
                ((Border)value).Child = element;
        }
        public static void ClipToBounds(this FrameworkElement element)
        {
            element.Clip = new RectangleGeometry { Rect = RectHelper.New(element.GetSize()) };
        }
        public static bool GetIsClipped(this FrameworkElement element)
        {
            return FrameworkElementHelper.GetIsClipped(element);
        }
        public static void SetIsClipped(this FrameworkElement element, bool value)
        {
            FrameworkElementHelper.SetIsClipped(element, value);
        }
        public static bool Contains(this FrameworkElement element, Point absolutePosition)
        {
            DependencyObject firstHit = null;
            VisualTreeHelper.HitTest(element,
                delegate(DependencyObject hitElement)
                {
                    firstHit = hitElement;
                    return HitTestFilterBehavior.Stop;
                },
                (hitTestResult) => HitTestResultBehavior.Stop,
                new PointHitTestParameters(element.MapPointFromScreen(absolutePosition)));
            return firstHit == element;
        }
        public static UIElement FindElement(this FrameworkElement element, Point absolutePosition, Func<UIElement, bool> condition)
        {
            UIElement result = null;
            VisualTreeHelper.HitTest(element,
                delegate(DependencyObject hitElement)
                {
                    result = hitElement as UIElement;
                    if (result == null || !condition(result))
                        return HitTestFilterBehavior.Continue;
                    else
                        return HitTestFilterBehavior.Stop;
                },
                (hitTestResult) => HitTestResultBehavior.Continue,
                new PointHitTestParameters(element.MapPointFromScreen(absolutePosition)));
            return result;
        }
        public static object GetToolTip(this FrameworkElement element)
        {
            return FrameworkElementHelper.GetToolTip(element);
        }
        public static void SetToolTip(this FrameworkElement element, object toolTip)
        {
            FrameworkElementHelper.SetToolTip(element, toolTip);
        }
        public static void ApplyStyleValuesToPropertiesWithLocalValues(this FrameworkElement element)
        {
            if (element.Style == null)
                return;
            foreach (Setter setter in element.Style.Setters)
                if (element.IsPropertyAssigned(setter.Property))
                    element.SetValue(setter.Property, setter.Value);
        }
    }
}
